package com.aco.fatureapp.service;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.collections4.IteratorUtils;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;

import android.annotation.SuppressLint;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import br.com.ac.fatureweb.integration.billing.FotografiaVO;
import br.com.ac.fatureweb.integration.enums.EnumSincronizacaoTabelas;
import br.com.ac.fatureweb.integration.model.CloseRouteVo;

import com.aco.fatureapp.enums.EnumProgress;
import com.aco.fatureapp.model.Fotografia;
import com.aco.fatureapp.model.Imovel;
import com.aco.fatureapp.model.Roteiro;
import com.aco.fatureapp.model.RoteiroConfig;
import com.aco.fatureapp.model.billing.LogCorrecaoConta;
import com.aco.fatureapp.model.billing.LogLeitura;
import com.aco.fatureapp.utilitary.FileUtils;
import com.aco.fatureapp.utilitary.JSonHttpRequest;
import com.aco.fatureapp.utilitary.JSonHttpRequest.EnumTipoRequisicao;
import com.aco.fatureapp.utilitary.ProgressAsyncTask;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.google.inject.Inject;
import com.orm.SugarApp;
import com.orm.SugarDb;
import com.orm.query.Select;

@SuppressLint("UseSparseArrays")
public class SrvCloseRoute {

	private JSonHttpRequest mJsonRequest = null;
	private List<NameValuePair> mParams = null;
	private SQLiteDatabase mDatabase = null;
	private ProgressAsyncTask mTask = null;
	private SQLiteDatabase database = null;

	@Inject
	private SrvExportRoute mSrvExportRoute;

	@Inject
	private SrvImportRoute mSrvImportRoute;

	@Inject
	public SrvCloseRoute() {
		super();
		this.mJsonRequest = new JSonHttpRequest();

		database = new SugarDb(SugarApp.getSugarContext())
				.getWritableDatabase();

		mParams = new ArrayList<NameValuePair>();
	}

	public String execute(List<NameValuePair> mParams,
			final ProgressAsyncTask mTask) throws Exception {

		this.mDatabase = new SugarDb(SugarApp.getSugarContext())
				.getWritableDatabase();

		this.mParams.addAll(mParams);
		this.mTask = mTask;

		if (JSonHttpRequest
				.webServiceIsAvailable(JSonHttpRequest.REQUEST_TYPE_CLOSE)) {

			this.mTask.myPublishProgress(EnumProgress.INICIO.getType());

			if (executeValidations()) {

				/*
				 * Verificando se todas as leituras foram enviadas.
				 */
				mJsonRequest.atualizarProgresso(mTask, 1, 0, 1,
						EnumSincronizacaoTabelas.RetornoPendente.getCodigo());

				List<Long> lstRetorno = validateReturnRoute();

				if (lstRetorno != null && !lstRetorno.isEmpty()) {
					String where = " INSCRICAO IN ( "
							+ buildWhereClause(lstRetorno) + " ) ";

					mSrvExportRoute.execute(mParams, this.mTask, where);
				}

				mJsonRequest.atualizarProgresso(mTask, 1, 1, 1,
						EnumSincronizacaoTabelas.RetornoPendente.getCodigo());

				/*
				 * Verificando se todas as correções foram enviadas.
				 */
				mJsonRequest
						.atualizarProgresso(mTask, 2, 0, 1,
								EnumSincronizacaoTabelas.CorrecoesPendentes
										.getCodigo());

				List<Long> lstLogCorrecao = validateLogCorrectionRoute();

				if (lstLogCorrecao != null && !lstLogCorrecao.isEmpty()) {
					String where = " INSCRICAO IN ( "
							+ buildWhereClause(lstLogCorrecao) + " ) ";

					mSrvExportRoute.execute(mParams, this.mTask, where);
				}

				mJsonRequest
						.atualizarProgresso(mTask, 2, 1, 1,
								EnumSincronizacaoTabelas.CorrecoesPendentes
										.getCodigo());

				List<NameValuePair> lstParams = new ArrayList<NameValuePair>();
				lstParams.add(mParams.get(0));
				lstParams.add(mParams.get(1));
				lstParams.add(mParams.get(2));
				lstParams.add(mParams.get(3));

				/*
				 * Enviando backup dos dados.
				 */
				mJsonRequest.atualizarProgresso(mTask, 3, 0, 0,
						EnumSincronizacaoTabelas.BackupRoteiro.getCodigo());

				String fileName = getBackupFileName();

				String fileDir = FileUtils.DIRETORIO_PADRAO
						+ FileUtils.DIRETORIO_DB_BACKUPS + "/" + fileName;

				FileUtils.backup(SugarApp.getSugarContext(), fileName);
				mJsonRequest.sendFiles(fileDir, null, lstParams, mTask, 3,
						EnumSincronizacaoTabelas.BackupRoteiro.getCodigo());

				List<NameValuePair> lstParams2 = new ArrayList<NameValuePair>();
				lstParams2.add(mParams.get(0));
				lstParams2.add(mParams.get(1));
				lstParams2.add(mParams.get(2));
				lstParams2.add(mParams.get(3));

				/*
				 * Enviando fotografias.
				 */
				mJsonRequest.atualizarProgresso(mTask, 4, 0, 0,
						EnumSincronizacaoTabelas.Fotografia.getCodigo());

				String sourceDir = FileUtils.DIRETORIO_PADRAO
						+ FileUtils.DIRETORIO_IMAGENS;

				String moveTo = FileUtils.DIRETORIO_PADRAO
						+ FileUtils.DIRETORIO_IMG_BACKUPS;

				mJsonRequest.sendFiles(sourceDir, moveTo, lstParams2, mTask, 4,
						EnumSincronizacaoTabelas.Fotografia.getCodigo());

				gerarRetornoFotografia();

				marcarRoteiroRetornado();

				// Liberar para nova carga.
				liberarCarga(mTask.getIdRoteiro());

				mTask.myPublishProgress(EnumProgress.FIM.getType());

				return JSonHttpRequest.SYNC_SUCCESSFUL;
			}
			else {
				return "Roteiro não está consistente para envio dos dados";
			}
		}
		else {
			throw new Exception(JSonHttpRequest.SERVICO_INDISPONIVEL);
		}
	}

	private void liberarCarga(Long idRoteiro) {
		RoteiroConfig roteiroConfig = getRoteiroConfig();

		if (roteiroConfig == null) {
			roteiroConfig = new RoteiroConfig();
			roteiroConfig.setNumero(idRoteiro);
		}

		roteiroConfig.setLeituraLiberada(false);
		roteiroConfig.setCargaLiberada(true);
		roteiroConfig.setEncerrado(true);
		roteiroConfig.save();
	}

	public RoteiroConfig getRoteiroConfig() {

		List<RoteiroConfig> roteiroConfig = Select.from(RoteiroConfig.class)
				.list();

		if (roteiroConfig != null && !roteiroConfig.isEmpty()) {
			roteiroConfig.iterator().next();
		}

		return null;
	}

	private void marcarRoteiroRetornado() throws Exception {

		mJsonRequest.execute(JSonHttpRequest.getClouseRouteURL()
				+ "marcarRoteiro", EnumTipoRequisicao.POST, mParams);
	}

	/**
	 *
	 * Verificar se existe correção de fatura não enviada, se sim: Enviar todo o
	 * retorno novamente para essas inscrições.
	 *
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	private List<Long> validateLogCorrectionRoute() throws Exception {

		List<NameValuePair> mParamsCheckLogCorrection = new ArrayList<NameValuePair>();
		List<CloseRouteVo> checkLogCorrection = new ArrayList<CloseRouteVo>();

		Iterator<LogCorrecaoConta> lstLogCorrecao = LogCorrecaoConta
				.findAll(LogCorrecaoConta.class);

		if (lstLogCorrecao != null && lstLogCorrecao.hasNext()) {

			for (final LogCorrecaoConta log : IteratorUtils
					.toList(lstLogCorrecao)) {

				CloseRouteVo closeRouteVo = new CloseRouteVo(
						log.getInscricao(), log.getInscricao());

				if (!checkLogCorrection.contains(closeRouteVo)) {
					checkLogCorrection.add(closeRouteVo);
				}
			}

			mParamsCheckLogCorrection.addAll(mParams);

			if (checkLogCorrection != null && !checkLogCorrection.isEmpty()) {
				mParamsCheckLogCorrection.add(new BasicNameValuePair(
						"lstRetorno", new Gson().toJson(checkLogCorrection,
								new TypeToken<ArrayList<CloseRouteVo>>() {
								}.getType())));

				List<Integer> lstTemp = (List<Integer>) mJsonRequest.execute(
						JSonHttpRequest.getClouseRouteURL()
								+ "validateLogCorrectionRoute",
						EnumTipoRequisicao.POST, mParamsCheckLogCorrection);

				if (lstTemp != null && !lstTemp.isEmpty()) {

					List<Long> lstRetorno = new ArrayList<Long>();

					for (Integer codigo : lstTemp) {
						lstRetorno.add(codigo.longValue());
					}

					return lstRetorno;
				}
			}
		}

		return null;
	}

	/**
	 *
	 * Verificar se existe dados de leitura não enviados, se sim: Enviar todo o
	 * retorno novamente para essas inscrições.
	 *
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	private List<Long> validateReturnRoute() throws Exception {

		List<NameValuePair> mParamsCheckReturnRoute = new ArrayList<NameValuePair>();
		List<CloseRouteVo> checkReturnRoute = new ArrayList<CloseRouteVo>();

		Iterator<Imovel> lstImovel = Imovel.findAll(Imovel.class);

		if (lstImovel != null && lstImovel.hasNext()) {

			for (final Imovel log : IteratorUtils.toList(lstImovel)) {
				checkReturnRoute.add(new CloseRouteVo(log.getInscricao(), log
						.getInscricao()));

			}

			mParamsCheckReturnRoute.addAll(mParams);
			if (checkReturnRoute != null && !checkReturnRoute.isEmpty()) {
				mParamsCheckReturnRoute.add(new BasicNameValuePair(
						"lstRetorno", new Gson().toJson(checkReturnRoute,
								new TypeToken<ArrayList<CloseRouteVo>>() {
								}.getType())));

				List<Integer> lstTemp = (List<Integer>) mJsonRequest.execute(
						JSonHttpRequest.getClouseRouteURL()
								+ "validateReturnRoute",
						EnumTipoRequisicao.POST, mParamsCheckReturnRoute);

				if (lstTemp != null && !lstTemp.isEmpty()) {

					List<Long> lstRetorno = new ArrayList<Long>();

					for (Integer codigo : lstTemp) {
						lstRetorno.add(codigo.longValue());
					}

					return lstRetorno;
				}
			}
		}

		return null;
	}

	/**
	 * Recuperar os dados de fotografia e exportar em formato JSON.
	 *
	 * @throws Exception
	 */
	private void gerarRetornoFotografia() throws Exception {

		List<NameValuePair> mParamsExportPhotos = new ArrayList<NameValuePair>();
		Iterator<Fotografia> lstRetornoFotografia = Fotografia
				.findAll(Fotografia.class);

		if (lstRetornoFotografia != null && lstRetornoFotografia.hasNext()) {
			String json = new Gson().toJson(
					IteratorUtils.toList(lstRetornoFotografia),
					new TypeToken<ArrayList<FotografiaVO>>() {
					}.getType());

			mParamsExportPhotos.addAll(mParams);
			mParamsExportPhotos.add(new BasicNameValuePair(
					"lstRetornoFotografia", json));

			mJsonRequest.execute(JSonHttpRequest.getClouseRouteURL()
					+ "receivePhotos", EnumTipoRequisicao.POST,
					mParamsExportPhotos);
		}
	}

	/**
	 * Executa as validação necessárias para verificar se o roteiro está apto
	 * para exportação.
	 *
	 * @return
	 * @throws Exception
	 */
	private Boolean executeValidations() throws Exception {
		// implements another validations here.
		long count = Roteiro.count(Roteiro.class, null, null);

		return count > 0;
	}

	/**
	 *
	 * Retorna o nome do arquivo de backup no seguinte formato:
	 * [idCarga]_[idRoteiro].dbbackup
	 *
	 * @return
	 */
	private String getBackupFileName() {
		Roteiro roteiro = null;
		Imovel imovel = null;

		Iterator<Roteiro> lstRoteiro = Roteiro.findAll(Roteiro.class);
		Iterator<Imovel> lstImovel = Imovel.findAll(Imovel.class);

		if ((lstRoteiro != null && lstRoteiro.hasNext())
				&& (lstImovel != null && lstImovel.hasNext())) {
			roteiro = lstRoteiro.next();
			imovel = lstImovel.next();

			return imovel.getIdCarga() + "_" + roteiro.getIdIntegracao()
					+ ".dbbackup";
		}

		return new SugarDb(SugarApp.getSugarContext()).getDatabaseName();
	}

	/**
	 * Método responsável por fechar a conexão com a base de dados.
	 */
	public void closeDB() {

		if (mDatabase != null) {
			mDatabase.close();
		}
	}

	public boolean isMeteringNotClose() {

		final Cursor cursor = database.rawQuery(
				"SELECT ID FROM " + Imovel.getTableName(Imovel.class)
						+ " IMO WHERE NOT EXISTS ( SELECT 1 FROM "
						+ LogLeitura.getTableName(LogLeitura.class)
						+ " LOG WHERE LOG.INSCRICAO = IMO.INSCRICAO )", null);

		cursor.moveToFirst();

		if (cursor.getCount() > 0) {

			cursor.close();
			return false;
		}

		cursor.close();
		return true;
	}

	/**
	 *
	 * Monta uma string formada pelas inscrições que tiveram correção separadas
	 * por vírgulas.
	 *
	 * @param lstInscricao
	 * @return
	 */
	private String buildWhereClause(final List<Long> lstInscricao) {
		final StringBuilder sb = new StringBuilder();

		for (final Long inscricao : lstInscricao) {
			sb.append(inscricao.toString());
			sb.append(", ");
		}

		return sb.toString().substring(0, sb.toString().lastIndexOf(","));
	}

	/**
	 * Marca a configuração do roteiro como encerrada e libera a carga.
	 */
	public void enableImport() {
		RoteiroConfig config = mSrvImportRoute.getRoteiroConfig();

		if (config != null) {
			config.setEncerrado(Boolean.TRUE);
			config.setCargaLiberada(Boolean.TRUE);
			config.setLeituraLiberada(Boolean.FALSE);
			config.save();
		}
	}
}
